package cn.yeamin.ms.config.shiro;

import java.util.HashMap;
import java.util.Map;

import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.web.filter.DelegatingFilterProxy;

/**
 * @packageName: 包名: cn.yeamin.ms.config.shiro
 * @classDesc: 类功能描述:(Shiro配置)
 * @author: Administrator
 * @createTime: 2017/7/24 15:37
 * @version: v1.0
 * @copyright:奕明(上海)影视传媒有限公司
 */
@Configuration
public class ShiroConfig
{


    /**
     * @methodDesc: 方法描述:(配置Realm)
     * @author: 李彤
     * @version: v1.0
     * @copyright:奕明(上海)影视传媒有限公司
     */
    @Bean(name = "realm")
    @ConditionalOnMissingBean
    public Realm realm() {
        return new MyShiroRealm();
    }



    /**
     * @methodDesc: 方法描述:(URL 级别权限控制,配置Filter,可以配置web.xml,但是是springboot项目可以声明成bean,filterName和ShiroFilterFactoryBean的Bean中的name要一致
     *              如果不配置或者过滤器顺序不对,就会报一下异常信息:
     *              org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton.
     *                This is an invalid application configuration.
     *              解决方案来源:http://www.cnblogs.com/ginponson/p/6217057.html
     *              )
     * @author: 李彤
     * @version: v1.0
     * @copyright:奕明(上海)影视传媒有限公司
     */
    @Bean
    public FilterRegistrationBean delegatingFilterProxy(){
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
        proxy.setTargetFilterLifecycle(true);
        //这个必须和ShiroFilterFactoryBean中Bean的name一致
        proxy.setTargetBeanName("shiroFilter");
        filterRegistrationBean.setFilter(proxy);
        return filterRegistrationBean;
    }

    /**
     * 用户授权信息Cache
     */
    @Bean(name = "shiroCacheManager")
    @ConditionalOnMissingBean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }


    /**
     * @methodDesc: 方法描述:(默认的安全管理器)
     * @author: 李彤
     * @version: v1.0
     * @copyright:奕明(上海)影视传媒有限公司
     */
    @Bean(name = "securityManager")
    @ConditionalOnMissingBean
    public DefaultSecurityManager securityManager() {
        DefaultSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setCacheManager(cacheManager());
        return securityManager;
    }



    /**
     * @methodDesc: 方法描述:(ShiroFilterFactoryBean处理拦截资源文件问题
     *              注意：单独一个ShiroFilterFactoryBean配置是或报错的，因为
     *              在初始化ShiroFilterFactoryBean的时候需要注入：
     *              Filter Chain定义说明:
     *               1、一个URL可以配置多个Filter，使用逗号分隔
     *               2、当设置多个过滤器时，全部验证通过，才视为通过
     *               3、部分过滤器可指定参数，如perms，roles
     * )
     * @author: 李彤
     * @version: v1.0
     * @copyright:奕明(上海)影视传媒有限公司
     */
    @Bean(name = "shiroFilter")
    @DependsOn("securityManager")
    @ConditionalOnMissingBean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager securityManager, Realm realm) {
        // 在安全管理器，应该注入Realm连接安全数据
        securityManager.setRealm(realm);
        //创建Shiro工厂类Bean对象:ShiroFilterFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 依赖于SecurityManager,必须设置
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 未登录时跳转地址,一般拦截登录
        shiroFilterFactoryBean.setLoginUrl("/admin/login");
        // 登录成功后要跳转地址
        shiroFilterFactoryBean.setSuccessUrl("/admin/index");
        //未授权(权限不足)跳转的地址
        shiroFilterFactoryBean.setUnauthorizedUrl("/previlige/403");
        //配置拦截链,URL控制路径和规则
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        //配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //配置访问规则:anon:所有url都都可以匿名访问,authc:所有url都必须认证通过才可以访问
        //filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/assets/**", "anon");
        filterChainDefinitionMap.put("/admin/login", "anon");
        filterChainDefinitionMap.put("/admin/user/**", "authc");
        filterChainDefinitionMap.put("/admin/role/**", "authc");
        filterChainDefinitionMap.put("/admin/resource/**", "authc");
        //有时候为了测试放开认证,正式环境必须加上
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }
}